﻿using System;
using System.Data;
using System.Text;

using BizElements.Core.ProviderComponents;

namespace BizElements.Core
{
    /// <summary>
    /// SQL statement base class. All SQL statement classes are derived from this class.
    /// Provides common SQL statement logic.
    /// </summary>
    [Serializable]
    public abstract class SqlStatementBase : DaoBase, IRenderSql
    {
        #region Private members.

        /// <summary>
        /// Alias of the table/view to which this entity belongs to.
        /// Required as distinct member when deserializing object to recreate member 'table'.
        /// </summary>
        private string tableAlias;

        /// <summary>
        /// Class that holds meta data for the table/view this entity belongs to.
        /// Required as distinct member when deserializing object to recreate member 'table'.
        /// </summary>
        private Type dbTableClass;

        /// <summary>
        /// Target table.
        /// Not serialized because it is very big. This member should never be acessed directly.
        /// Use TargetTable property as it will recreate this member if it's null (eg. after deserialization).
        /// </summary>
        [NonSerialized]
        private IDbTable table;

        #endregion

        #region Protected members.

        /// <summary>
        /// Target table.
        /// </summary>
        protected IDbTable TargetTable
        {
            get
            {
                // Member 'table' may be null after deserialization. It has to be recreated.
                if (this.table == null)
                    this.table = DbTableFactory.CreateTable(this.dbTableClass, this.tableAlias);

                return this.table;
            }
            set
            {
                this.table = value;

                // Set info required by Activator.
                this.tableAlias = (value.Alias != value.TableName) ? value.Alias : this.tableAlias = null;
                this.dbTableClass = value.GetType();
            }
        }

        #endregion

        #region Constructors.

        /// <summary>
        /// Constructor. Initializes target table and the ConnectionProvider.
        /// </summary>
        /// <param name="targetTable">Target table.</param>
        protected SqlStatementBase(IDbTable targetTable)
            : base(targetTable.Catalog)
        {
            this.TargetTable = targetTable;
        }

        #endregion

        #region Methods.

        /// <summary>
        /// Renders SQL statement.
        /// </summary>
        /// <param name="dbms">Target DBMS.</param>
        /// <param name="output">StringBuilder to which SQL is appended.</param>
        /// <param name="parameters">SQL parameter collection to which the object's and its children's
        /// parameters are added. After the rendering is done the collection contains all parameters with unique names.</param>
        public abstract void Render(DbmsType dbms, StringBuilder output, DbParameterCollection parameters);

        /// <summary>
        /// Returns SQL code for this statement. Uses syntax for the object's ConnectionProvider.
        /// </summary>
        /// <returns>SQL command text.</returns>
        public override string ToString()
        {
            return ToString(this.ConnectionProvider.DBMS);
        }

        /// <summary>
        /// Returns SQL code for this statement. Uses syntax for the given DBMS.
        /// </summary>
        /// <param name="dbms">DBMS.</param>
        /// <returns>SQL command text.</returns>
        public string ToString(DbmsType dbms)
        {
            DbParameterCollection parameters = new DbParameterCollection();
            StringBuilder cmdText = new StringBuilder();
            Render(dbms, cmdText, parameters);
            return cmdText.ToString();
        }

        #endregion
    }
}